package xj.graph2d.shapes;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import xj.graph2d.DrawAttr;
import xj.graph2d.DrawShape;
import xj.graph2d.DrawShapeBase;
import xj.graph2d.StrokeMap;
import xj.lang.CodeWriter;
import xj.util.gui.Point2DFloat;

public class ScribbleShape 
  extends DrawShapeBase {

  protected Point2D start;

  protected List<Point2D> points; // coordinates relative to start

  public ScribbleShape() {
    points = new ArrayList<Point2D>();
  }

  @Override
  public Object clone() 
    throws CloneNotSupportedException {
    ScribbleShape s = new ScribbleShape();
    copyTo(s);
    if (start != null) {
      s.start = new Point2DFloat((float) start.getX(), (float) start.getY());
    }
    int count = points.size();
    for (int i = 0; i < count; i++) {
      Point2D p = points.get(i);
      s.points.add(new Point2DFloat((float) p.getX(), (float) p.getY()));
    }
    return s;
  }

  public DrawShape makeInstance() {
    return new ScribbleShape();
  }

  @Override
  public String getName() {
    return "Scribble";
  }

  public ScribbleShape appendPoint(Point2D p) {
    return appendPoint(p, true);
  }

  public ScribbleShape appendPoint(Point2D p, boolean absolute) {
    if (p != null) {
      if (start == null) {
	start = p;
      } else {
	if (absolute) {
	  p = new Point2DFloat((float) (p.getX() - start.getX()), (float) (p
	      .getY() - start.getY()));
	}
	points.add(p);
      }
    }
    return this;
  }

  public Point2D getPoint(int i) {
    if (i == 0) {
      return start;
    } else if (i >= 1 && i <= points.size()) {
      return points.get(i - 1);
    }
    return null;
  }

  public int getPointCount() {
    return (points.size() + 1);
  }

  @Override
  public void move(float dx, float dy) {
    start.setLocation(start.getX() + dx, start.getY() + dy);
    calculateBounds();
  }

  @Override
  public void setEnds(float x1, float y1, float x2, float y2) {
    p1.setLocation(x1, y1);
    p2.setLocation(x2, y2);
    normalize();
  }

  @Override
  public void drawShape(Graphics2D g2, int mode, 
			DrawShape selectedShape,
			Set<DrawShape> selectionSet) {
    DrawAttr attr = getDrawAttr();
    Stroke oldStroke = null;
    Stroke stk = null;
    Color oldColor = g2.getColor();
    if (attr != null) {
      g2.setColor(attr.getPenColor());
      StrokeMap smap = StrokeMap.getInstance();
      stk = smap.findStroke(attr.getPenSize(), attr.getLineCap(), attr
	  .getLineJoin());
    } else {
      g2.setColor(DrawAttr.getDefaultPenColor());
    }
    if (stk != null) {
      oldStroke = g2.getStroke();
      g2.setStroke(stk);
    }
    draw(g2);
    g2.setColor(oldColor);
    if (oldStroke != null && stk != null) {
      g2.setStroke(oldStroke);
    }
  }

  @Override
  public void draw(Graphics2D g2) {
    if (start != null) {
      double x0 = start.getX();
      double y0 = start.getY();
      double px = x0;
      double py = y0;
      int count = points.size();
      for (int i = 0; i < count; i++) {
	Point2D p = points.get(i);
	double x = p.getX() + x0;
	double y = p.getY() + y0;
	g2.draw(new Line2D.Double(px, py, x, y));
	px = x;
	py = y;
      }
    }
  }

  @Override
  public void calculateBounds() {
    if (start == null) {
      bounds.setRect(0.0, 0.0, 0.0, 0.0);
      framebounds.setRect(0.0, 0.0, 0.0, 0.0);
    } else {
      double minX, minY, maxX, maxY;
      minX = maxX = 0;
      minY = maxY = 0;
      int count = points.size();
      for (int i = 0; i < count; i++) {
	Point2D p = points.get(i);
	minX = Math.min(minX, p.getX());
	minY = Math.min(minY, p.getY());
	maxX = Math.max(maxX, p.getX());
	maxY = Math.max(maxY, p.getY());
      }
      p1.setLocation(start.getX() + minX, start.getY() + minY);
      p2.setLocation(start.getX() + maxX, start.getY() + maxY);
      bounds.setRect(start.getX() + minX, start.getY() + minY, maxX - minX,
	  maxY - minY);
      framebounds.setRect(bounds);
      adjustBounds();
    }
  }

  /*
         * public void writeXML(CodeWriter out, int mode, String
         * namespacePrefix) { if (out != null) { String nodename =
         * getPrefixedName("ScribbleShape", namespacePrefix); out.indentInc();
         * out.println("<" + nodename + " x=\"" + start.getX() + "\" y=\"" +
         * start.getY() + "\">"); writeXMLBase(out, mode, namespacePrefix);
         * writeXMLPoints(out, points, namespacePrefix); out.thisIndentDec();
         * out.println("</" + nodename + ">"); } }
         */

  @Override
  protected Map<String, Object> getXMLAttributes() {
    Map<String, Object> attrs = super.getXMLAttributes();
    if (attrs == null) {
      attrs = new HashMap<String, Object>();
    }
    attrs.put("x", start.getX());
    attrs.put("y", start.getY());
    return attrs;
  }

  @Override
  protected void writeXMLElements(CodeWriter out, int mode,
      String namespacePrefix) {
    if (out != null) {
      super.writeXMLElements(out, mode, namespacePrefix);

      writeXMLPoints(out, points, namespacePrefix);
    }
  }

  @Override
  protected void processXMLAttributes(org.w3c.dom.Element node) {
    if (node != null) {
      super.processXMLAttributes(node);

      String xs, ys;
      float x, y;
      xs = node.getAttribute("x");
      ys = node.getAttribute("y");
      try {
	x = Float.parseFloat(xs);
	y = Float.parseFloat(ys);
	if (start != null) {
	  start.setLocation(x, y);
	} else {
	  start = new Point2DFloat(x, y);
	}
      } catch (NumberFormatException ex) {
      }
    }
  }

  @Override
  protected void processXMLElement(org.w3c.dom.Element e, String namespaceURI) {
    if (e != null) {
      if ("Points".equals(e.getLocalName())) {
	org.w3c.dom.NodeList pnodes = e.getChildNodes();
	if (pnodes != null) {
	  int pn = pnodes.getLength();
	  for (int j = 0; j < pn; j++) {
	    org.w3c.dom.Node pnode = pnodes.item(j);
	    if (pnode.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE
		&& "Point".equals(pnode.getLocalName())) {
	      org.w3c.dom.Element pe = (org.w3c.dom.Element) pnode;
	      String xs, ys;
	      float x, y;
	      xs = pe.getAttribute("x");
	      ys = pe.getAttribute("y");
	      try {
		x = Float.parseFloat(xs);
		y = Float.parseFloat(ys);
		points.add(new Point2DFloat(x, y));
	      } catch (NumberFormatException ex) {
	      }
	    }
	  }
	}
      } else {
	super.processXMLElement(e, namespaceURI);
      }
    }
  }

  /*
         * public void setAttrFromXML(org.w3c.dom.Element node, String
         * namespaceURI) { if (node != null) { String uri =
         * node.getNamespaceURI(); if (namespaceURI == null ||
         * namespaceURI.equals(uri)) { super.setAttrFromXML(node, namespaceURI);
         * 
         * String xs, ys; float x, y; xs = node.getAttribute("x"); ys =
         * node.getAttribute("y"); try { x = Float.parseFloat(xs); y =
         * Float.parseFloat(ys); start = new Point2DFloat(x, y); } catch
         * (NumberFormatException ex) {}
         * 
         * org.w3c.dom.NodeList nodes = node.getChildNodes(); if (nodes != null) {
         * int n = nodes.getLength(); for (int i = 0; i < n; i++) {
         * org.w3c.dom.Node cnode = nodes.item(i); if (cnode.getNodeType() ==
         * org.w3c.dom.Node.ELEMENT_NODE &&
         * "Points".equals(cnode.getLocalName())) { if (debug) {
         * System.out.println("ScribbleShape.setAttrFromXML(): <Points> tag"); }
         * 
         * org.w3c.dom.NodeList pnodes = cnode.getChildNodes(); if (pnodes !=
         * null) { int pn = pnodes.getLength(); for (int j = 0; j < pn; j++) {
         * org.w3c.dom.Node pnode = pnodes.item(j); if (pnode.getNodeType() ==
         * org.w3c.dom.Node.ELEMENT_NODE &&
         * "Point".equals(pnode.getLocalName())) { org.w3c.dom.Element pe =
         * (org.w3c.dom.Element) pnode; xs = pe.getAttribute("x"); ys =
         * pe.getAttribute("y"); if (debug) {
         * System.out.println("ScribbleShape.setAttrFromXML(): <Point x=\"" + xs +
         * "\" y=\"" + ys + "\"/>"); } try { x = Float.parseFloat(xs); y =
         * Float.parseFloat(ys); points.add(new Point2DFloat(x, y)); } catch
         * (NumberFormatException ex) {} } } } } } } calculateBounds(); } } }
         */

}
